package com.linjiahao.oss.util;

import com.linjiahao.oss.vo.BasicType;
import org.springframework.util.ReflectionUtils;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 实体属性和方法工具类
 * @author suruihuang
 */
public class StateSecretEntityUtil {
    //方法Map,key:类的calss完整路径(class.getName())+.+方法名(如:setXXX,getYYY),value:Method实例
    public static Map<String, Method> methodMap=new HashMap<String, Method>();

    //字段Map,key:类的calss完整路径(class.getName())+.+字段名,value:Field实例
    public static Map<String, Field> fieldMap=new HashMap<String, Field>();

    //是否已经加载过
    private static Boolean isLoad=false;

    /**
     * 缓存类信息(方法、成员)
     * @param classList
     * @throws IntrospectionException
     * @throws ClassNotFoundException
     */
    public static void cacheClassInfo(List<String> classList,Map<String, Set<String>> classFieldMap) throws IntrospectionException, ClassNotFoundException {
        synchronized (isLoad){
            if(isLoad==false){
                isLoad=true;
            }else{
                return;
            }
        }

        if(classList!=null){
            for(String classStr:classList){
                Class c = Class.forName(classStr);
                cacheClass(c,classFieldMap);
                //System.out.println(c.toString());//class cn.com.do1.component.test.SubClass
                //System.out.println(c.getPackage());//package cn.com.do1.component.test
                //System.out.println(c.getName());//cn.com.do1.component.test.SubClass
            }
        }
    }

    /**
     * 缓存Class信息
     * @param clazz
     */
    private static void cacheClass(Class clazz,Map<String, Set<String>> classFieldMap) throws IntrospectionException {
        Field[] tableFields = clazz.getDeclaredFields();
        Class<?> superClazz = clazz.getSuperclass();
        if (superClazz.equals(Object.class)) {
            cacheMethodAndField(tableFields,clazz,classFieldMap);
        } else {
            Field[] tableSuperFields = superClazz.getDeclaredFields();
            Field[] superFields = new Field[tableFields.length + tableSuperFields.length];
            System.arraycopy(tableFields, 0, superFields, 0, tableFields.length);
            System.arraycopy(tableSuperFields, 0, superFields, tableFields.length, tableSuperFields.length);
            Field[] allFields = getSuperClassFields(superFields, superClazz);
            cacheMethodAndField(allFields,clazz,classFieldMap);
        }
    }

    /**
     * 缓存方法和字段
     * @param allFields
     * @param clazz
     * @throws IntrospectionException
     */
    private static void cacheMethodAndField(Field[] allFields,Class clazz,Map<String, Set<String>> classFieldMap) throws IntrospectionException {
        Field field=null;
        Method getMethod=null;
        Method setMethod=null;

        String className=clazz.getName();
        Set<String> fieldSet=classFieldMap.get(className);
        if(fieldSet==null||fieldSet.size()==0){
            return;
        }

        for (int i = 0; i < allFields.length; i++) {
            field=allFields[i];

            //如果不是配置文件中需要加解密、签名、验签相关的字段，则不需要缓存类的字段、set、get方法
            if(fieldSet.contains(field.getName())==false){
                continue;
            }

            PropertyDescriptor pd = new PropertyDescriptor(field.getName(), clazz);
            //获得get方法
            getMethod = pd.getReadMethod();
            //获得set方法
            setMethod = pd.getWriteMethod();

            methodMap.put(clazz.getName()+"."+getMethod.getName(),getMethod);
            methodMap.put(clazz.getName()+"."+setMethod.getName(),setMethod);
            fieldMap.put(clazz.getName()+"."+field.getName(),field);
        }
    }

    /**
     * 获取父类的所有字段
     * @param tableFields
     * @param clazz
     * @return
     */
    private static Field[] getSuperClassFields(Field[] tableFields, Class<?> clazz) {
        Class<?> superClazz = clazz.getSuperclass();
        if (superClazz.equals(Object.class)) {
            return tableFields;
        }
        Field[] tableSuperFields = superClazz.getDeclaredFields();

        Field[] c = new Field[tableFields.length + tableSuperFields.length];
        System.arraycopy(tableFields, 0, c, 0, tableFields.length);
        System.arraycopy(tableSuperFields, 0, c, tableFields.length, tableSuperFields.length);
        getSuperClassFields(c, superClazz);
        return c;
    }

    /**
     * 根据字段名获取get方法名
     * @param key
     * @return
     */
    public static String getMethodName(String key){
        return new StringBuilder().append("get")
                .append(key.substring(0, 1).toUpperCase())
                .append(key.substring(1)).toString();
    }

    /**
     * 根据字段名获取set方法名
     * @param key
     * @return
     */
    public static String setMethodName(String key){
        return new StringBuilder().append("set")
                .append(key.substring(0, 1).toUpperCase())
                .append(key.substring(1)).toString();
    }

    /**
     * 执行get方法，获取数值
     * @param obj 对象实例
     * @param fieldName 字段名,需要加密的字段名，驼峰规范
     * @return
     */
    public static Object invokeGetMethod(Object obj,String fieldName){
        StringBuilder key=new StringBuilder();
        key.append(obj.getClass().getName());
        key.append(".");
        key.append(getMethodName(fieldName));
        Method getMethod= com.linjiahao.oss.util.StateSecretEntityUtil.methodMap.get(key.toString());
        Object value = ReflectionUtils.invokeMethod(getMethod, obj);
        return isBaseTypeToString(value);
    }

    /**
     * 执行get方法,获取值,支持获取一个对象多个字段的值,值与fieldNames数组下标一一对应
     * @param obj 对象实例
     * @param fieldNames 字段名数组,需要加密的字段名，驼峰规范
     * @return
     */
    public static Object[] invokeGetMethod(Object obj,String fieldNames[]){
        if(obj==null||fieldNames==null){
            return null;
        }

        Object [] returnObjs=new Object[fieldNames.length];

        StringBuilder prefixKey=new StringBuilder();
        prefixKey.append(obj.getClass().getName());
        prefixKey.append(".");

        StringBuilder key=null;
        String fieldName=null;
        for(int i=0;i<fieldNames.length;i++){
            fieldName=fieldNames[i];

            key=new StringBuilder();
            key.append(prefixKey);
            key.append(getMethodName(fieldName));

            Method getMethod= com.linjiahao.oss.util.StateSecretEntityUtil.methodMap.get(key.toString());
            Object value = ReflectionUtils.invokeMethod(getMethod, obj);
            returnObjs[i]=isBaseTypeToString(value);
        }
        return returnObjs;
    }

    /**
     * 执行get方法,获取值,支持获取多个对象多个字段的值
     * @param objs 对象数组
     * @param fieldNames 字段数组
     * @return 返回数组,下标[0][x]:表示第1个对象的某一个字段值
     */
    public static Object[][] invokeGetMethod(Object objs[],String fieldNames[]){
        if(objs==null||fieldNames==null||objs.length==0||fieldNames.length==0){
            return null;
        }

        StringBuilder prefixKey=new StringBuilder();
        prefixKey.append(objs[0].getClass().getName());
        prefixKey.append(".");

        StringBuilder key=null;
        String fieldName=null;

        //求方法数组
        Method getMethods[]=new Method[fieldNames.length];
        for(int i=0;i<fieldNames.length;i++){
            fieldName=fieldNames[i];

            key=new StringBuilder();
            key.append(prefixKey);
            key.append(getMethodName(fieldName));

            getMethods[i]= com.linjiahao.oss.util.StateSecretEntityUtil.methodMap.get(key.toString());
        }

        Object obj=null;
        //返回结果数组
        Object results[][]=new Object[objs.length][fieldNames.length];
        for(int i=0;i<objs.length;i++){
            obj=objs[i];

            for(int j=0;j<fieldNames.length;j++){
                Object value = ReflectionUtils.invokeMethod(getMethods[j], obj);
                results[i][j]=isBaseTypeToString(value);
            }
        }

        return results;
    }

    /**
     * 执行set方法
     * @param obj 对象实例
     * @param fieldName 字段名,需要加密的字段名，驼峰规范
     * @param value 需要set的值
     */
    public static void invokeSetMethod(Object obj,String fieldName,Object value){
        if(obj==null||fieldName==null){
            return;
        }
        StringBuilder key=new StringBuilder();
        key.append(obj.getClass().getName());
        key.append(".");
        key.append(setMethodName(fieldName));
        Method setMethod= com.linjiahao.oss.util.StateSecretEntityUtil.methodMap.get(key.toString());
        ReflectionUtils.invokeMethod(setMethod, obj,value);
    }

    /**
     * 执行set方法,支持同时设置一个对象多个字段
     * @param obj 需要修改值的对象
     * @param fieldNames 字段数组
     * @param values 值数组
     */
    public static void invokeSetMethod(Object obj,String fieldNames[],Object values[]){
        if(obj==null||fieldNames==null||values==null||fieldNames.length==0||values.length==0){
            return;
        }
        if(fieldNames.length!=values.length){
            return;
        }

        String className=obj.getClass().getName();
        StringBuilder prefixKey=new StringBuilder();
        prefixKey.append(className);
        prefixKey.append(".");

        StringBuilder key=null;
        String fieldName=null;
        for(int i=0;i<fieldNames.length;i++) {
            fieldName=fieldNames[i];

            key=new StringBuilder();
            key.append(prefixKey);
            key.append(setMethodName(fieldName));

            Method setMethod= com.linjiahao.oss.util.StateSecretEntityUtil.methodMap.get(key.toString());
            ReflectionUtils.invokeMethod(setMethod, obj,values[i]);
        }
    }

    /**
     * 执行set方法,支持同时设置多个对象多个字段
     * @param objs 对象数组
     * @param fieldNames 字段数组
     * @param values 设置的值[0][x]表示第1个对象某个字段
     */
    public static void invokeSetMethod(Object objs[],String fieldNames[],Object values[][]){
        if(objs==null||fieldNames==null||values==null||objs.length==0||fieldNames.length==0){
            return;
        }
        //值的行数与对象行数需要相等(行)
        if(values.length!=objs.length){
            return;
        }
        //字段的数量和值的数量需要相等(列)
        if(values[0].length!=fieldNames.length){
            return;
        }

        String className=objs[0].getClass().getName();
        StringBuilder prefixKey=new StringBuilder();
        prefixKey.append(className);
        prefixKey.append(".");

        StringBuilder key=null;
        String fieldName=null;
        //求设置值方法数组
        Method setMethods[]=new Method[fieldNames.length];
        for(int i=0;i<fieldNames.length;i++) {
            fieldName=fieldNames[i];

            key=new StringBuilder();
            key.append(prefixKey);
            key.append(setMethodName(fieldName));

            setMethods[i]= com.linjiahao.oss.util.StateSecretEntityUtil.methodMap.get(key.toString());
        }

        Object obj=null;
        for(int i=0;i<objs.length;i++){
            obj=objs[i];

            for(int j=0;j<fieldNames.length;j++) {
                ReflectionUtils.invokeMethod(setMethods[j], obj,values[i][j]);
            }
        }
    }

    /**
     * 判断是否基础类型和包装类型进行String转换
     * @param value 字段值类型
     */
    public static Object isBaseTypeToString(Object value) {
        Object result = null;
        if (value == null) {
            result = "";
            return result;
        }
        Class<?> valueClass = value.getClass();
        if (valueClass.isPrimitive() || BasicType.wrapperPrimitiveMap.containsKey(valueClass)) {
            result = String.valueOf(value);
            //String.valueOf 当转换为空会返回null
            if ("null".equals(result)) {
                result = "";
            }
        } else {
            result = (String) value;
        }
        return result;
    }

}
